package net.sf.ivyide.idea;

import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.module.JavaModuleType;
import com.intellij.openapi.module.ModifiableModuleModel;
import com.intellij.openapi.module.Module;
import com.intellij.openapi.progress.ProgressIndicator;
import com.intellij.openapi.progress.ProgressManager;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.roots.ModifiableRootModel;
import com.intellij.openapi.roots.ModuleRootManager;
import com.intellij.openapi.roots.impl.libraries.ProjectLibraryTable;
import com.intellij.openapi.roots.libraries.Library;
import com.intellij.openapi.roots.libraries.LibraryTable;
import com.intellij.openapi.roots.libraries.LibraryTablesRegistrar;
import com.intellij.util.ui.update.MergingUpdateQueue;
import com.intellij.util.ui.update.Update;
import net.sf.ivyide.ModuleManager;
import net.sf.ivyide.ModuleManagerFactory;
import net.sf.ivyide.ResolvedModule;

import java.io.File;
import java.util.*;

public class UpdateModules implements ProgressAwareCallable<Void> {

    private static final Logger LOGGER = Logger.getInstance(UpdateModules.class.getName());

    private Project m_project;
    private ModuleManagerFactory m_moduleManagerFactory;
    private static final String GENERATE_BY_IVY = "generate.by.ivy";

    public UpdateModules(final ModuleManagerFactory moduleManagerFactory, Project project) {
        m_moduleManagerFactory = moduleManagerFactory;
        m_project = project;
    }

    public Void call(final ProgressIndicator indicator) throws Exception {
        indicator.setText("Initializing ivy..");

        ModuleManager moduleManager = m_moduleManagerFactory.createModuleManager();
        List<ResolvedModule> resolvedModules = new ModuleResolver().getResolvedModules(moduleManager, indicator);

        final String projectDir = IntellijUtil.getBaseDir(m_project);
        final Map<String, Library> projectLibrariesUsedByAnyModule = new HashMap<String, Library>();
        double maxResolves = resolvedModules.size();
        double syncedCount = 0;
        indicator.setIndeterminate(false);
        indicator.setFraction(0.001);
        final Set<Module> usedModules = new HashSet<Module>();
        LOGGER.info("Starting sync");
        final SyncContext syncContext = new SyncContext(m_project);
        //first make sure all modules (eg new ones) are known. This prevents issues with forward dependencies to modules that are not defined yet.
        Map<ResolvedModule, Module> ivyModuleToIntellijModule = new HashMap<ResolvedModule, Module>();
        for (final ResolvedModule resolvedModule : resolvedModules) {
            final String modulePath = new File(projectDir, "ivymodules/" + resolvedModule.getModuleId().getName() + ".iml").getPath();
            Module moduleByPath = syncContext.getModulesModel().newModule(modulePath, new JavaModuleType());
            moduleByPath.setOption(GENERATE_BY_IVY, "true");
            usedModules.add(moduleByPath);
            //moduleByPath.setSavePathsRelative(true);
            final ModifiableRootModel modifiableModel = syncContext.getModuleModel(moduleByPath);
            //setup root model
            modifiableModel.inheritSdk();
            ivyModuleToIntellijModule.put(resolvedModule, moduleByPath);
        }
        for (final ResolvedModule resolvedModule : resolvedModules) {
            Module module = ivyModuleToIntellijModule.get(resolvedModule);
            final UpdateModule updateModule = new UpdateModule(moduleManager, resolvedModule, syncContext.getModuleModel(module), projectLibrariesUsedByAnyModule, null, syncContext);
            updateModule.run(indicator);
            double fraction = ++syncedCount / maxResolves;
            indicator.setFraction(fraction);
        }
        LOGGER.info("Cleaning ");

        for (Module module : syncContext.getModulesModel().getModules()) {
            if (!usedModules.contains(module)) {
                if ("true".equals(module.getOptionValue(GENERATE_BY_IVY))) {
                    syncContext.getModulesModel().disposeModule(module);
                }
            }
        }
        indicator.setText("Applying changes to project...");
        indicator.setIndeterminate(true);

        UpdateModule.cleanUnusedLibraries(projectLibrariesUsedByAnyModule.values(), syncContext.getProjectLibraryTableModel());
        ProgressUtilities.executeAsWriteActionOnEventQueue(new ProgressAwareRunnable() {

            public void run(ProgressIndicator indicator) {
                syncContext.commit();
            }
        }, indicator);
        LOGGER.info("Done");
        return null;
    }

}
